﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Nest;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace OpenData.WebApi.Controllers
{
    [Route("[controller]/[action]")]
    public class ElasticSearchController : ControllerBase
    {
        private readonly IndexConfig _indexConfig;

        public ElasticSearchController(IOptions<IndexConfig> indexConfig)
        {
            _indexConfig = indexConfig.Value;
        }
        [HttpGet]
        public async Task<ActionResult<ESList>> GetList(string keyWord, int pageIndex = 1, int pageSize = 10)
        {
            var node = new Uri(_indexConfig.Server);
            var settings = new ConnectionSettings(node);
            var client = new ElasticClient(settings);
            var resultAsync = await Task.WhenAll(_indexConfig.IndexList.Select(x => Search(x.Index, keyWord, client)));
            var result = resultAsync.Where(x => x.Any());
            var total = result.Count();
            var data = result.Select(x =>
            {
                var first = x.FirstOrDefault();
                var item = new EsItem
                {
                    Content = first.Content,
                    Index = first.Index,
                    KeyWords = x.Select(s => s.KeyWords).SelectMany(n => n).ToList(),
                    Score = x.Sum(s => s.Score) / x.Count(),
                    Title = first.Title
                };

                return item;
            }).OrderByDescending(x => x.Score).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            return new ESList
            {
                Data = data,
                Total = total
            };
        }
        [HttpGet]
        public async Task<ActionResult<KeywordResponse>> GetKeyWord(string key)
        {
            var url = new Uri(string.Format(_indexConfig.KeywordUrl, key));
            var request = (HttpWebRequest)WebRequest.Create(url);
            var response = await request.GetResponseAsync();
            var reader = new StreamReader(response.GetResponseStream());
            var array = (JArray)JObject.Parse(reader.ReadToEnd())["g"];
            return new KeywordResponse { KeyWordList = array.Select(x => x["q"].ToString()) };
        }
        private async Task<IEnumerable<EsItem>> Search(string index, string keyWord, ElasticClient client)
        {
            var conrainer = new QueryContainer();
            var search = _indexConfig.IndexList.FirstOrDefault(x => x.Index == index);
            var highlight = new Dictionary<Field, IHighlightField>();
            search.Highlight.ForEach(x => highlight.Add(x, new HighlightField()));
            foreach (var item in search.SearchFields)
                conrainer = conrainer || new QueryStringQuery { DefaultField = item.FieldName, Boost = item.Boost, Query = keyWord };
            var queryModel = new SearchRequest<Dictionary<string, object>>(index)
            {
                Query = conrainer,
                Highlight = new Highlight
                {
                    PreTags = new[] { "<span>" },
                    PostTags = new[] { "</span>" },
                    Fields = highlight
                }
            };
            var response = await client.SearchAsync<Dictionary<string, object>>(queryModel);
            var result = response.Hits.Select(x => new { x.Score, x.Id, x.Index, x.Source, x.Highlight });
            return result.Select(x => new EsItem
            {
                Index = index,
                Score = x.Score,
                Title = search.Title,
                Content = search.Content,
                KeyWords = x.Highlight.Select(x =>
                {
                    var regex = new Regex("<[a-zA-Z]+.*?>([\\s\\S]*?)</[a-zA-Z]*?>");
                    var input = string.Join(",", x.Value);
                    var rs = regex.Matches(input).Select(x => x.Value.Replace("<span>", "").Replace("</span>", ""));
                    return rs.Where(x => x.Length > 1);
                }).SelectMany(x => x).ToList()
            });
        }
    }
}
